/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the Source EULA. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------------------------
 *---------------------------------------------------------------------------------------------
 *---------------------------------------------------------------------------------------------
 *---------------------------------------------------------------------------------------------
 *---------------------------------------------------------------------------------------------
 * Please make sure to make edits in the .ts file at https://github.com/Microsoft/vscode-loader/
 *---------------------------------------------------------------------------------------------
 *---------------------------------------------------------------------------------------------
 *---------------------------------------------------------------------------------------------
 *---------------------------------------------------------------------------------------------
 *--------------------------------------------------------------------------------------------*/
'use strict';
var _cssPluginGlobal = this;
var CSSBuildLoaderPlugin;
(function (CSSBuildLoaderPlugin) {
	var global = _cssPluginGlobal || {};
	/**
	 * Known issue:
	 * - In IE there is no way to know if the CSS file loaded successfully or not.
	 */
	var BrowserCSSLoader = (function () {
		function BrowserCSSLoader() {
			this._pendingLoads = 0;
		}
		BrowserCSSLoader.prototype.attachListeners = function (name, linkNode, callback, errorback) {
			var unbind = function () {
				linkNode.removeEventListener('load', loadEventListener);
				linkNode.removeEventListener('error', errorEventListener);
			};
			var loadEventListener = function (e) {
				unbind();
				callback();
			};
			var errorEventListener = function (e) {
				unbind();
				errorback(e);
			};
			linkNode.addEventListener('load', loadEventListener);
			linkNode.addEventListener('error', errorEventListener);
		};
		BrowserCSSLoader.prototype._onLoad = function (name, callback) {
			this._pendingLoads--;
			callback();
		};
		BrowserCSSLoader.prototype._onLoadError = function (name, errorback, err) {
			this._pendingLoads--;
			errorback(err);
		};
		BrowserCSSLoader.prototype._insertLinkNode = function (linkNode) {
			this._pendingLoads++;
			var head = document.head || document.getElementsByTagName('head')[0];
			var other = head.getElementsByTagName('link') || document.head.getElementsByTagName('script');
			if (other.length > 0) {
				head.insertBefore(linkNode, other[other.length - 1]);
			}
			else {
				head.appendChild(linkNode);
			}
		};
		BrowserCSSLoader.prototype.createLinkTag = function (name, cssUrl, externalCallback, externalErrorback) {
			var _this = this;
			var linkNode = document.createElement('link');
			linkNode.setAttribute('rel', 'stylesheet');
			linkNode.setAttribute('type', 'text/css');
			linkNode.setAttribute('data-name', name);
			var callback = function () { return _this._onLoad(name, externalCallback); };
			var errorback = function (err) { return _this._onLoadError(name, externalErrorback, err); };
			this.attachListeners(name, linkNode, callback, errorback);
			linkNode.setAttribute('href', cssUrl);
			return linkNode;
		};
		BrowserCSSLoader.prototype._linkTagExists = function (name, cssUrl) {
			var i, len, nameAttr, hrefAttr, links = document.getElementsByTagName('link');
			for (i = 0, len = links.length; i < len; i++) {
				nameAttr = links[i].getAttribute('data-name');
				hrefAttr = links[i].getAttribute('href');
				if (nameAttr === name || hrefAttr === cssUrl) {
					return true;
				}
			}
			return false;
		};
		BrowserCSSLoader.prototype.load = function (name, cssUrl, externalCallback, externalErrorback) {
			if (this._linkTagExists(name, cssUrl)) {
				externalCallback();
				return;
			}
			var linkNode = this.createLinkTag(name, cssUrl, externalCallback, externalErrorback);
			this._insertLinkNode(linkNode);
		};
		return BrowserCSSLoader;
	}());
	var NodeCSSLoader = (function () {
		function NodeCSSLoader() {
			this.fs = require.nodeRequire('fs');
		}
		NodeCSSLoader.prototype.load = function (name, cssUrl, externalCallback, externalErrorback) {
			var contents = this.fs.readFileSync(cssUrl, 'utf8');
			// Remove BOM
			if (contents.charCodeAt(0) === NodeCSSLoader.BOM_CHAR_CODE) {
				contents = contents.substring(1);
			}
			externalCallback(contents);
		};
		return NodeCSSLoader;
	}());
	NodeCSSLoader.BOM_CHAR_CODE = 65279;
	// ------------------------------ Finally, the plugin
	var CSSPlugin = (function () {
		function CSSPlugin(cssLoader) {
			this.cssLoader = cssLoader;
		}
		CSSPlugin.prototype.load = function (name, req, load, config) {
			config = config || {};
			var myConfig = config['vs/css'] || {};
			global.inlineResources = myConfig.inlineResources;
			global.inlineResourcesLimit = myConfig.inlineResourcesLimit || 5000;
			var cssUrl = req.toUrl(name + '.css');
			this.cssLoader.load(name, cssUrl, function (contents) {
				// Contents has the CSS file contents if we are in a build
				if (config.isBuild) {
					CSSPlugin.BUILD_MAP[name] = contents;
					CSSPlugin.BUILD_PATH_MAP[name] = cssUrl;
				}
				load({});
			}, function (err) {
				if (typeof load.error === 'function') {
					load.error('Could not find ' + cssUrl + ' or it was empty');
				}
			});
		};
		CSSPlugin.prototype.write = function (pluginName, moduleName, write) {
			// getEntryPoint is a Monaco extension to r.js
			var entryPoint = write.getEntryPoint();
			// r.js destroys the context of this plugin between calling 'write' and 'writeFile'
			// so the only option at this point is to leak the data to a global
			global.cssPluginEntryPoints = global.cssPluginEntryPoints || {};
			global.cssPluginEntryPoints[entryPoint] = global.cssPluginEntryPoints[entryPoint] || [];
			global.cssPluginEntryPoints[entryPoint].push({
				moduleName: moduleName,
				contents: CSSPlugin.BUILD_MAP[moduleName],
				fsPath: CSSPlugin.BUILD_PATH_MAP[moduleName],
			});
			write.asModule(pluginName + '!' + moduleName, 'define([\'vs/css!' + entryPoint + '\'], {});');
		};
		CSSPlugin.prototype.writeFile = function (pluginName, moduleName, req, write, config) {
			if (global.cssPluginEntryPoints && global.cssPluginEntryPoints.hasOwnProperty(moduleName)) {
				var fileName = req.toUrl(moduleName + '.css');
				var contents = [
					'/*---------------------------------------------------------',
					' * Copyright (c) Microsoft Corporation. All rights reserved.',
					' *--------------------------------------------------------*/'
				], entries = global.cssPluginEntryPoints[moduleName];
				for (var i = 0; i < entries.length; i++) {
					if (global.inlineResources) {
						contents.push(Utilities.rewriteOrInlineUrls(entries[i].fsPath, entries[i].moduleName, moduleName, entries[i].contents, global.inlineResources === 'base64', global.inlineResourcesLimit));
					}
					else {
						contents.push(Utilities.rewriteUrls(entries[i].moduleName, moduleName, entries[i].contents));
					}
				}
				write(fileName, contents.join('\r\n'));
			}
		};
		CSSPlugin.prototype.getInlinedResources = function () {
			return global.cssInlinedResources || [];
		};
		return CSSPlugin;
	}());
	CSSPlugin.BUILD_MAP = {};
	CSSPlugin.BUILD_PATH_MAP = {};
	CSSBuildLoaderPlugin.CSSPlugin = CSSPlugin;
	var Utilities = (function () {
		function Utilities() {
		}
		Utilities.startsWith = function (haystack, needle) {
			return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle;
		};
		/**
		 * Find the path of a file.
		 */
		Utilities.pathOf = function (filename) {
			var lastSlash = filename.lastIndexOf('/');
			if (lastSlash !== -1) {
				return filename.substr(0, lastSlash + 1);
			}
			else {
				return '';
			}
		};
		/**
		 * A conceptual a + b for paths.
		 * Takes into account if `a` contains a protocol.
		 * Also normalizes the result: e.g.: a/b/ + ../c => a/c
		 */
		Utilities.joinPaths = function (a, b) {
			function findSlashIndexAfterPrefix(haystack, prefix) {
				if (Utilities.startsWith(haystack, prefix)) {
					return Math.max(prefix.length, haystack.indexOf('/', prefix.length));
				}
				return 0;
			}
			var aPathStartIndex = 0;
			aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, '//');
			aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'http://');
			aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'https://');
			function pushPiece(pieces, piece) {
				if (piece === './') {
					// Ignore
					return;
				}
				if (piece === '../') {
					var prevPiece = (pieces.length > 0 ? pieces[pieces.length - 1] : null);
					if (prevPiece && prevPiece === '/') {
						// Ignore
						return;
					}
					if (prevPiece && prevPiece !== '../') {
						// Pop
						pieces.pop();
						return;
					}
				}
				// Push
				pieces.push(piece);
			}
			function push(pieces, path) {
				while (path.length > 0) {
					var slashIndex = path.indexOf('/');
					var piece = (slashIndex >= 0 ? path.substring(0, slashIndex + 1) : path);
					path = (slashIndex >= 0 ? path.substring(slashIndex + 1) : '');
					pushPiece(pieces, piece);
				}
			}
			var pieces = [];
			push(pieces, a.substr(aPathStartIndex));
			if (b.length > 0 && b.charAt(0) === '/') {
				pieces = [];
			}
			push(pieces, b);
			return a.substring(0, aPathStartIndex) + pieces.join('');
		};
		Utilities.commonPrefix = function (str1, str2) {
			var len = Math.min(str1.length, str2.length);
			for (var i = 0; i < len; i++) {
				if (str1.charCodeAt(i) !== str2.charCodeAt(i)) {
					break;
				}
			}
			return str1.substring(0, i);
		};
		Utilities.commonFolderPrefix = function (fromPath, toPath) {
			var prefix = Utilities.commonPrefix(fromPath, toPath);
			var slashIndex = prefix.lastIndexOf('/');
			if (slashIndex === -1) {
				return '';
			}
			return prefix.substring(0, slashIndex + 1);
		};
		Utilities.relativePath = function (fromPath, toPath) {
			if (Utilities.startsWith(toPath, '/') || Utilities.startsWith(toPath, 'http://') || Utilities.startsWith(toPath, 'https://')) {
				return toPath;
			}
			// Ignore common folder prefix
			var prefix = Utilities.commonFolderPrefix(fromPath, toPath);
			fromPath = fromPath.substr(prefix.length);
			toPath = toPath.substr(prefix.length);
			var upCount = fromPath.split('/').length;
			var result = '';
			for (var i = 1; i < upCount; i++) {
				result += '../';
			}
			return result + toPath;
		};
		Utilities._replaceURL = function (contents, replacer) {
			// Use ")" as the terminator as quotes are oftentimes not used at all
			return contents.replace(/url\(\s*([^\)]+)\s*\)?/g, function (_) {
				var matches = [];
				for (var _i = 1; _i < arguments.length; _i++) {
					matches[_i - 1] = arguments[_i];
				}
				var url = matches[0];
				// Eliminate starting quotes (the initial whitespace is not captured)
				if (url.charAt(0) === '"' || url.charAt(0) === '\'') {
					url = url.substring(1);
				}
				// The ending whitespace is captured
				while (url.length > 0 && (url.charAt(url.length - 1) === ' ' || url.charAt(url.length - 1) === '\t')) {
					url = url.substring(0, url.length - 1);
				}
				// Eliminate ending quotes
				if (url.charAt(url.length - 1) === '"' || url.charAt(url.length - 1) === '\'') {
					url = url.substring(0, url.length - 1);
				}
				if (!Utilities.startsWith(url, 'data:') && !Utilities.startsWith(url, 'http://') && !Utilities.startsWith(url, 'https://')) {
					url = replacer(url);
				}
				return 'url(' + url + ')';
			});
		};
		Utilities.rewriteUrls = function (originalFile, newFile, contents) {
			return this._replaceURL(contents, function (url) {
				var absoluteUrl = Utilities.joinPaths(Utilities.pathOf(originalFile), url);
				return Utilities.relativePath(newFile, absoluteUrl);
			});
		};
		Utilities.rewriteOrInlineUrls = function (originalFileFSPath, originalFile, newFile, contents, forceBase64, inlineByteLimit) {
			var fs = require.nodeRequire('fs');
			var path = require.nodeRequire('path');
			return this._replaceURL(contents, function (url) {
				if (/\.(svg|png)$/.test(url)) {
					var fsPath = path.join(path.dirname(originalFileFSPath), url);
					var fileContents = fs.readFileSync(fsPath);
					if (fileContents.length < inlineByteLimit) {
						global.cssInlinedResources = global.cssInlinedResources || [];
						var normalizedFSPath = fsPath.replace(/\\/g, '/');
						if (global.cssInlinedResources.indexOf(normalizedFSPath) >= 0) {
							console.warn('CSS INLINING IMAGE AT ' + fsPath + ' MORE THAN ONCE. CONSIDER CONSOLIDATING CSS RULES');
						}
						global.cssInlinedResources.push(normalizedFSPath);
						var MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png';
						var DATA = ';base64,' + fileContents.toString('base64');
						if (!forceBase64 && /\.svg$/.test(url)) {
							// .svg => url encode as explained at https://codepen.io/tigt/post/optimizing-svgs-in-data-uris
							var newText = fileContents.toString()
								.replace(/"/g, '\'')
								.replace(/</g, '%3C')
								.replace(/>/g, '%3E')
								.replace(/&/g, '%26')
								.replace(/#/g, '%23')
								.replace(/\s+/g, ' ');
							var encodedData = ',' + newText;
							if (encodedData.length < DATA.length) {
								DATA = encodedData;
							}
						}
						return '"data:' + MIME + DATA + '"';
					}
				}
				var absoluteUrl = Utilities.joinPaths(Utilities.pathOf(originalFile), url);
				return Utilities.relativePath(newFile, absoluteUrl);
			});
		};
		return Utilities;
	}());
	CSSBuildLoaderPlugin.Utilities = Utilities;
	(function () {
		var cssLoader = null;
		var isElectron = (typeof process !== 'undefined' && typeof process.versions !== 'undefined' && typeof process.versions['electron'] !== 'undefined');
		if (typeof process !== 'undefined' && process.versions && !!process.versions.node && !isElectron) {
			cssLoader = new NodeCSSLoader();
		}
		else {
			cssLoader = new BrowserCSSLoader();
		}
		define('vs/css', new CSSPlugin(cssLoader));
	})();
})(CSSBuildLoaderPlugin || (CSSBuildLoaderPlugin = {}));
